Segmentation is a memory management scheme wherein a microprocessor allows software to define multiple variable-size "segments" of memory, and then limits access to particular segments based upon the type of operation being performed. Definable segments include a code segment for storing instruction data, a stack segment for storing stack data (i.e., operand data for macroinstructions such as PUSH, POP and CALL), and one or more data segments for storing program data. Once the segments have been defined, the microprocessor accesses individual segments by default based upon the type of operation being performed. For example, fetches of instruction data by the microprocessor ("code-fetches" or "fetches") are normally performed from the code segment. Programmers can thereby reference memory by specifying an offset into a default (or a designated) segment, without being concerned with the actual physical memory location/s being accessed.
Microprocessors that use segmentation typically have one segment register for each type of segment supported. The segment registers are loaded by an operating system prior to execution of an application program. The value loaded into each segment register specifies a starting byte address or "segment base address" for the respective segment, and an upper-most byte address or "limit address" for the segment. For purposes of this application, it may be assumed that a set of segment base registers is used to hold the segment base addresses, and that a set of limit address registers is used to hold the limit addresses.
Once the segment registers have been loaded and program execution is initiated, a typical microprocessor that uses segmentation will operate as follows. A central processing unit (CPU) of the microprocessor generates fetch commands for retrieving instructions from memory, and generates operand access commands for performing data and stack accesses to memory. With each fetch command or operand access command, the CPU generates an effective address and indicates a selected segment. The CPU also generates size bits to indicate the number of bytes to be accessed. A segmentation unit of the microprocessor adds each effective address generated by the CPU to the segment base address for the selected segment to generate a linear address. Each effective address thereby serves as an offset into a selected segment.
A paging unit of the microprocessor may optionally modify a fixed number of high-order bits of the linear address to produce a physical address. Physical addresses are used to access an internal cache, and may be placed on an external bus to access physical memory (and/or an external cache memory). If paging is not used, linear addresses generated by the segmentation unit are used as physical addresses.
Each time the microprocessor performs an access to memory (or cache), the microprocessor performs an address limit check to ensure that memory is not being accessed outside the selected segment. Limit checking catches certain programming errors such as runaway subscripts and invalid pointer calculations. To perform the limit check, the microprocessor uses the limit address specified by the appropriate limit address register to determine whether one or more byte locations will be accessed that fall outside the selected segment. Addresses held by the limit address registers are effective addresses.
If a limit error is detected, the microprocessor responds differently depending upon the type of access being performed. If the limit error is detected on an operand write, for example, the microprocessor will abort the write operation, thereby preventing data outside the segment from being corrupted. The microprocessor will then generate an exception and enter into a limit-error handling routine. If the limit error is detected on a code-fetch, the microprocessor will generate an exception only if the instruction data for which the limit error was detected is actually executed. This prevents limit exceptions from being generated for data that is fetched from memory but not executed by the CPU.
The conventional method for performing limit checks for both code and operand accesses is to perform the check in effective address space. To perform a limit check on an operand access ("operand limit check") by the conventional method, a limit check circuit uses the effective address generated by the CPU along with size bits to determine the effective addresses of the byte locations to be accessed. These effective addresses are compared with the effective limit address specified by the appropriate limit address register.
The conventional method for performing address limit checks for fetch accesses ("fetch limit checks") is more complicated. Since microprocessors commonly perform fetches as N-byte aligned accesses, the limit check circuit must additionally determine which N-byte aligned block or "line" of fetch data will be accessed in physical memory once the segment base address is added in. Although this determination is relatively simple when the segment base address is also N-byte aligned, the addition of a significant quantity of logic is required to account for the possibility that non-aligned segment base addresses will used. This additional logic uses the X low-order bits of the segment base address to determine which N-byte line will actually be referenced by the linear address, where 2.sup.x =N.
The present invention presents an alternative method for performing fetch limit checks which significantly reduces the complexity of the limit check circuit. In describing the present invention, reference will be made to address sizes and certain methods for performing fetch accesses. It should be understood, however, that such references are being made only to describe one exemplary microprocessor to which the present invention may be applied, and are not intended to limit the scope of the invention.